#[macro_use]
extern crate rocket;
extern crate diesel;
extern crate rocket_sync_db_pools;

use rocket::{
  response::status,
  serde::json::{json, Json, Value},
};

mod auth;
mod mapper;
mod models;
mod schema;

use auth::BasicAuth;
use mapper::TodoMapper;
use models::{NewTodo, UpdateTodo};

use diesel::sqlite::SqliteConnection;
use rocket_sync_db_pools::database;

// 使用对应的Rocket.toml 的配置
#[database("sqlite")]
struct DbConn(SqliteConnection);

#[get("/")]
fn hello() -> Value {
  json!("Hello, World!")
}

// curl -X GET 127.0.0.1:8000/todo/ -H "Authorization: Basic aGVsbG8gd29ybGQ="
#[get("/todo")]
async fn get_todo(_auth: BasicAuth, db: DbConn) -> Value {
  db.run(|conn| {
    let result = TodoMapper::find_multiple(conn, 100)
      .expect("Failed to read todo entries.");
    json!(result)
  })
  .await
  // json!([
  //   {"id": 1, "name": "Timly"},
  //   {"id": 2, "name": "Jerry"}
  // ])
}

#[get("/todo/<id>")]
async fn view_todo(_auth: BasicAuth, db: DbConn, id: i32) -> Value {
  db.run(move |conn| {
    let result =
      TodoMapper::find(conn, id).expect("Failed retrieving todo row.");
    json!(result)
  })
  .await

  // json!(
  //   {"id": id, "name": "Timly", "email": "timly@gmail.com"}
  // )
}

// curl -X POST 127.0.0.1:8000/todo -H "Authorization: Basic aGVsbG8gd29ybGQ=" -H "Content-Type: application/json" -d '{"name": "Timly","email": "aaa@qq.com"}'
#[post("/todo", format = "json", data = "<new_todo>")]
async fn create_todo(
  _auth: BasicAuth,
  db: DbConn,
  new_todo: Json<NewTodo>,
) -> Value {
  db.run(|conn| {
    let result = TodoMapper::create(conn, new_todo.into_inner())
      .expect("Failed insertting new todo entry.");

    json!(result)
  })
  .await
  // json!(
  //   {"id": 3, "name": "Tom", "email": "tom@gamil.com"}
  // )
}

// curl -X PUT 127.0.0.1:8000/todo/2 -H "Content-Type: application/json" -H "Authorization: Basic aGVsbG8gd29ybGQ=" -d '{"name": "tom", "email": "abc@139.com"}'
#[put("/todo/<id>", format = "json", data = "<todo>")]
async fn update_todo(
  _auth: BasicAuth,
  db: DbConn,
  id: i32,
  todo: Json<UpdateTodo>,
) -> Value {
  db.run(move |conn| {
    let result = TodoMapper::update(conn, id, todo.into_inner())
      .expect("Failed updating todo entry.");

    json!(result)
  })
  .await
  // json!(
  //   {"id": id, "name": "Tom_Tom", "email": "tom_tomj@gamil.com"}
  // )
}

// curl -X DELETE 127.0.0.1:8000/todo/1 -H "Authorization: Basic aGVsbG8gd29ybGQ="
#[delete("/todo/<id>")]
async fn delete_todo(
  _auth: BasicAuth,
  db: DbConn,
  id: i32,
) -> status::NoContent {
  db.run(move |conn| {
    TodoMapper::delete(conn, id).expect("Failed deleting todo entry.");

    status::NoContent
  })
  .await
}

#[catch(404)]
fn not_found() -> Value {
  json!("Not Found!")
}

#[rocket::main]
async fn main() {
  // let server = rocket::build().mount("/", routes!(hello));
  let server = rocket::build()
    .mount(
      "/",
      routes![
        hello,
        get_todo,
        view_todo,
        create_todo,
        update_todo,
        delete_todo
      ],
    )
    .register("/", catchers![not_found])
    .attach(DbConn::fairing());

  server.launch().await.unwrap();
}

// 0.5 后可以如下写法
// #[launch]
// fn rocket() -> _ {
//   rocket::build().mount("/hello", routes![hello])
// }

// cargo install diesel_cli
// https://diesel.rs/guides/getting-started.html
// cargo install diesel_cli --no-default-features --features sqlite

// diesel setup --database-url ./database.sqlite

// diesel migration list

// diesel migration generate create_todo

// diesel migration list --database-url=database.sqlite

// diesel migration run --database-url=database.sqlite

// diesel migration list --database-url=database.sqlite

// diesel migration revert --database-url=database.sqlite

// diesel migration run --database-url=database.sqlite
